package com.zhdl.network.demotcp.dto;

import com.project.common.util.ByteHelper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

/**
 * MemoBus协议组装
 */
public class MemoBusPkg {
    /**
     * 命令序列号
     */
    private static byte iSerial = 0;

    /**
     * 获得发送命令序号，最大255，超过255时从0开始
     *
     * @return
     */
    public static byte GetSerial() {
        iSerial++;
        if (iSerial > 255)
            iSerial = 0;

        return iSerial;
    }

    /**
     * @param unitBuff
     * @return
     * @throws IOException
     */
    private byte[] PkgFullFrame(byte[] unitBuff) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        bw.write((byte) 0x11);      // 扩展MEMOBUS(指令)
        // 设定命令序列号(每次发送时递增)
        bw.write(GetSerial());
        bw.write((byte) 0x00); //设定发送目标通道编号 PLC侧通道不固定，因此可固定为0
        bw.write((byte) 0x00); // 设定发送源通道编号 电脑中无通道概念，因此固定为0
        bw.write((byte) 0x00); // 预约
        bw.write((byte) 0x00); // 预约
        // 设定总数据数(218报头起始至MEMOBUS数据末尾)
        // 数据总长度 = 218报头(12字节) + 数据单元长度 （Length部(2字节) + MFC + SFC + ... ）
        int fullLen = 12 + unitBuff.length;
        bw.write((byte) (fullLen & 0x00FF));// 数据长度(L)
        bw.write((byte) ((fullLen & 0xFF00) >> 8));// 数据长度(H)
        bw.write((byte) 0x00); // 预约
        bw.write((byte) 0x00); // 预约
        bw.write((byte) 0x00); // 预约
        bw.write((byte) 0x00); // 预约
        // 具体指令的数据单元部分
        bw.write(unitBuff);

        return bw.toByteArray();
    }

    /**
     * 得到按钮控制字节,MB类型，一共两个字节
     *
     * @param status
     */
    public byte[] getBtnCtlBuff(AxisBtnStatus status) {
        int[] temp = new int[16];
        temp[0] = status.isEnable() ? 1 : 0;
        temp[1] = status.isAlarmReset() ? 1 : 0;
        temp[2] = status.isJog() ? 1 : 0;
        temp[3] = status.isResJog() ? 1 : 0;
        temp[5] = status.isTurnOrg() ? 1 : 0;
        temp[6] = status.isAbsPosition() ? 1 : 0;
        temp[7] = status.isStop() ? 1 : 0;
        temp[8] = status.isStepMove() ? 1 : 0;

        StringBuilder str = new StringBuilder();
        for (int i = 0; i < 16; i++)
            str.append(temp[15 - i]);

        String str1 = str.substring(0, 8);
        String str2 = str.substring(8, 16);
        byte[] bytes = new byte[2];
        bytes[0] = ByteHelper.bitString2Byte(str2);
        bytes[1] = ByteHelper.bitString2Byte(str1);
        return bytes;
    }

    /**
     * 得到按钮控制字节,MB类型，一共两个字节
     *
     * @param status
     */
    public byte[] getBtnCtlBuff2(AxisBtnStatus status) {
        int[] temp = new int[16];
        temp[0] = status.isEnable() ? 1 : 0;
        temp[1] = status.isAlarmReset() ? 1 : 0;
        temp[2] = status.isJog() ? 1 : 0;
        temp[3] = status.isResJog() ? 1 : 0;
        temp[5] = status.isTurnOrg() ? 1 : 0;
        temp[6] = status.isAbsPosition() ? 1 : 0;
        temp[7] = status.isStop() ? 1 : 0;
        temp[8] = status.isStepMove() ? 1 : 0;

        StringBuilder str = new StringBuilder();
        for (int i = 0; i < 16; i++)
            str.append(temp[15 - i]);

        String str1 = str.substring(0, 8);
        String str2 = str.substring(8, 16);
        byte[] bytes = new byte[4];
        bytes[0] = ByteHelper.bitString2Byte(str2);
        bytes[1] = ByteHelper.bitString2Byte(str1);
        return bytes;
    }

    /**
     * 0x01 读取线圈的状态
     *
     * @param startAddress
     * @param readNum
     */
    public byte[] ReadCoilStatus(short startAddress, short readNum) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x07;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x01);    //SFC 读取线圈的状态
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        // 参考编号 读取的起始位置
        bw.write((byte) ((startAddress & 0xFF00) >> 8));// Adr(H)
        bw.write((byte) (startAddress & 0x00FF));// Adr(L)
        // 线圈数量
        bw.write((byte) ((readNum & 0xFF00)) >> 8); // DataNum(H)
        bw.write((byte) (readNum & 0x00FF)); // DataNum(L)


        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x05 单一线圈的状态的改变
     *
     * @param startAddress
     * @param writeValue
     */
    public byte[] WriteOneCoilStatus(short startAddress, boolean writeValue) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x07;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x05);    //SFC 单一线圈的状态的改变
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        // 参考编号 写入的起始位置
        bw.write((byte) ((startAddress & 0xFF00) >> 8));// Adr(H)
        bw.write((byte) (startAddress & 0x00FF));// Adr(L)
        // 写入值
        if (writeValue) {
            bw.write((byte) 0xFF);
        } else {
            bw.write((byte) 0x00);
        }
        bw.write((byte) 0x00);

        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x09 读取保持寄存器的内容(扩展)
     *
     * @param startAddress
     * @param readNum      寄存器个数，一寄存器占2个字，4个字节
     */
    public byte[] ReadKeepRegisterExd(short startAddress, short readNum) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x08;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x09);    //SFC 读取保持寄存器的内容(扩展)
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        bw.write((byte) 0x00); // Spare固定为0
        // 参考编号
        bw.write((byte) (startAddress & 0x00FF));// Adr(L)
        bw.write((byte) ((startAddress & 0xFF00) >> 8));// Adr(H)
        // 寄存器数量
        bw.write((byte) (readNum & 0x00FF)); // DataNum(L)
        bw.write((byte) ((readNum & 0xFF00)) >> 8); // DataNum(H)

        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x0B 写入保持寄存器的内容(扩展)
     *
     * @param startAddress
     * @param regCount
     */
    public byte[] WriteKeepRegisterExd(short startAddress, short regCount, byte[] buff) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x08 + regCount * 2;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x0B);    //SFC 写入保持寄存器的内容(扩展)
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        bw.write((byte) 0x00); // Spare固定为0
        // 参考编号
        bw.write((byte) (startAddress & 0x00FF));// Adr(L)
        bw.write((byte) ((startAddress & 0xFF00) >> 8));// Adr(H)
        // 寄存器数量
        bw.write((byte) (regCount & 0x00FF)); // DataNum(L)
        bw.write((byte) ((regCount & 0xFF00)) >> 8); // DataNum(H)
        // 写入寄存器的数据
        bw.write(buff);

        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x0D 读取不连续保持寄存器(扩展)
     *
     * @param startAddress
     */
    public byte[] ReadManyKeepRegisterExd(short[] startAddress) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x06 + startAddress.length * 2;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x0D);    //SFC 保持寄存器的不连续读取(扩展)
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        bw.write((byte) 0x00); // Spare固定为0
        // 寄存器数量
        bw.write((byte) (startAddress.length & 0x00FF)); // DataNum(L)
        bw.write((byte) ((startAddress.length & 0xFF00)) >> 8); // DataNum(H)
        // 参考编号
        for (int i = 0; i < startAddress.length; i++) {
            bw.write((byte) (startAddress[i] & 0x00FF));// Adr(L)
            bw.write((byte) ((startAddress[i] & 0xFF00) >> 8));// Adr(H)
        }

        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x0E 保持寄存器的不连续写入(扩展)
     *
     * @param map
     */
    public byte[] WriteManyKeepRegisterExd(Map<Short, byte[]> map) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x06 + map.size() * 4;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x0E);    //SFC 保持寄存器的不连续读取(扩展)
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        bw.write((byte) 0x00); // Spare固定为0
        // 寄存器数量
        bw.write((byte) (map.size() & 0x00FF)); // DataNum(L)
        bw.write((byte) ((map.size() & 0xFF00)) >> 8); // DataNum(H)
        for (Short key : map.keySet()) {
            byte[] value = map.get(key);
            // 参考编号
            bw.write((byte) (key & 0x00FF));// Adr(L)
            bw.write((byte) ((key & 0xFF00) >> 8));// Adr(H)
            // 写入寄存器的数据
            bw.write(value);
        }

        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x0E 保持寄存器的不连续写入(扩展)
     * 写入状态字节，同时写入速度距离等字排字节
     */
    public byte[] WriteManyLKeepRegisterExd(short statusAddr, byte[] statusBuff, Map<Short, byte[]> map) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x06 + map.size() * 8 + 4;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x0E);    //SFC 保持寄存器的不连续读取(扩展)
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        bw.write((byte) 0x00); // Spare固定为0
        // 寄存器数量,L类型占2字
        bw.write((byte) ((map.size() * 2 + 1) & 0x00FF)); // DataNum(L)
        bw.write((byte) (((map.size() * 2 + 1) & 0xFF00)) >> 8); // DataNum(H)

        // 按钮地址
        bw.write((byte) (statusAddr & 0x00FF));// Adr(L)
        bw.write((byte) ((statusAddr & 0xFF00) >> 8));// Adr(H)

        // 写入寄存器的数据
        bw.write(statusBuff);

        for (Short key : map.keySet()) {
            // 地址
            bw.write((byte) (key & 0x00FF));// Adr(L)
            bw.write((byte) ((key & 0xFF00) >> 8));// Adr(H)

            byte[] value = map.get(key);

            // 写入寄存器的数据
            bw.write(value[0]);
            bw.write(value[1]);

            // 地址
            bw.write((byte) ((key + 1) & 0x00FF));// Adr(L)
            bw.write((byte) (((key + 1) & 0xFF00) >> 8));// Adr(H)

            // 写入寄存器的数据
            bw.write(value[2]);
            bw.write(value[3]);
        }

        return PkgFullFrame(bw.toByteArray());
    }


    /**
     * 0x0E 保持寄存器的不连续写入(扩展)
     * 写入状态字节，同时写入速度距离等字排字节
     */
    public byte[] WriteManyLKeepRegisterExd(Map<Short, byte[]> map) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x06 + map.size() * 8;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x0E);    //SFC 保持寄存器的不连续读取(扩展)
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        bw.write((byte) 0x00); // Spare固定为0
        // 寄存器数量,L类型占2字
        bw.write((byte) ((map.size() * 2 + 1) & 0x00FF)); // DataNum(L)
        bw.write((byte) (((map.size() * 2 + 1) & 0xFF00)) >> 8); // DataNum(H)

        for (Short key : map.keySet()) {
            // 地址
            bw.write((byte) (key & 0x00FF));// Adr(L)
            bw.write((byte) ((key & 0xFF00) >> 8));// Adr(H)

            byte[] value = map.get(key);

            // 写入寄存器的数据
            bw.write(value[0]);
            bw.write(value[1]);

            // 地址
            bw.write((byte) ((key + 1) & 0x00FF));// Adr(L)
            bw.write((byte) (((key + 1) & 0xFF00) >> 8));// Adr(H)

            // 写入寄存器的数据
            bw.write(value[2]);
            bw.write(value[3]);
        }

        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x10 写入多个连续保持寄存器的内容
     *
     * @param startAddress
     * @param regCount
     */
    public byte[] WriteContKeepRegisterExd(short startAddress, short regCount, byte[] buff) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        int unitFrmLen = 0x08 + regCount * 2;
        bw.write((byte) unitFrmLen);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x20);     // MFC固定为0x20
        bw.write((byte) 0x0B);    //SFC 写入保持寄存器的内容(扩展)
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        bw.write((byte) 0x00); // Spare固定为0
        // 参考编号
        bw.write((byte) (startAddress & 0x00FF));// Adr(L)
        bw.write((byte) ((startAddress & 0xFF00) >> 8));// Adr(H)
        // 寄存器数量
        bw.write((byte) (regCount & 0x00FF)); // DataNum(L)
        bw.write((byte) ((regCount & 0xFF00)) >> 8); // DataNum(H)
        // 写入寄存器的数据
        bw.write(buff);

        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 读取寄存器的状态(32位地址)
     *
     * @param startAddress
     * @param regCount
     */
    public byte[] Read32bitRegisterStatus(short startAddress, short regCount) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();

        // 创建MEMOBUS数据部
        // Length为MFC至数据末尾
        bw.write((byte) 0x0C);         // MEMOBUS数据长度(L)
        bw.write((byte) 0x00);  // MEMOBUS数据长度(H)
        bw.write((byte) 0x43);     // MFC固定为0x43
        bw.write((byte) 0x49);    //SFC
        bw.write((byte) 0x10); // 自身的CPU编号固定为0
        bw.write((byte) 0x00); // 空
        bw.write((byte) 0x47); // 寄存器种类
        bw.write((byte) 0x00); // 空

        // 参考编号
        bw.write((byte) (startAddress & 0x00FF));// Adr(L)
        bw.write((byte) ((startAddress & 0xFF00) >> 8));// Adr(H)
        bw.write((byte) 0x00);
        bw.write((byte) 0x00);
        // 寄存器数量
        bw.write((byte) (regCount & 0x00FF)); // DataNum(L)
        bw.write((byte) ((regCount & 0xFF00)) >> 8); // DataNum(H)

        return PkgFullFrame(bw.toByteArray());
    }
}
